package com.leige.utils.tool;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import com.leige.utils.setting.Const;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.ConnectionPool;
import com.squareup.okhttp.Dispatcher;
import com.squareup.okhttp.FormEncodingBuilder;
import com.squareup.okhttp.Interceptor;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.MultipartBuilder;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.ResponseBody;

import okio.BufferedSink;

/**
 * 定义HTTP请求管理相关方法,如果返回的是Response，要记得调用关闭的方法
 */
public final class HttpClient {
    public static final String ContentTypeHeader = "Content-Type";
    public static final String DefaultMime = "application/octet-stream";
    public static final String JsonMime = "application/json";
    public static final String FormMime = "application/x-www-form-urlencoded";
    private final OkHttpClient httpClient;
    public static HttpClient create() {
		// TODO Auto-generated method stub
    	return new HttpClient();
	}
    public static HttpClient create(final boolean isSSL) {
		// TODO Auto-generated method stub
    	return new HttpClient(isSSL);
	}
    public HttpClient() {
        Dispatcher dispatcher = new Dispatcher();
        dispatcher.setMaxRequests(64);
        dispatcher.setMaxRequestsPerHost(16);
        ConnectionPool connectionPool = new ConnectionPool(32, 5 * 60 * 1000);
        httpClient = new OkHttpClient();
        httpClient.setDispatcher(dispatcher);
        httpClient.setConnectionPool(connectionPool);
        httpClient.networkInterceptors().add(new Interceptor() {
            
            @Override
			public com.squareup.okhttp.Response intercept(final Chain chain) throws IOException {
                Request request = chain.request();
                com.squareup.okhttp.Response response = chain.proceed(request);
                IpTag tag = (IpTag) request.tag();
                String ip = chain.connection().getSocket().getRemoteSocketAddress().toString();
                tag.ip = ip;
                return response;
            }
        });
        httpClient.setConnectTimeout(10000, TimeUnit.SECONDS);
        httpClient.setReadTimeout(5000, TimeUnit.SECONDS);
        httpClient.setWriteTimeout(5000, TimeUnit.SECONDS);
    }
    public HttpClient(final boolean isSSL){
    	this();
    	if(isSSL) {
			try {
				this.setSSL();
			} catch (KeyManagementException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (NoSuchAlgorithmException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
    }
    public void setSSL() throws NoSuchAlgorithmException, KeyManagementException {
		// TODO Auto-generated method stub
    	SSLContext sc = SSLContext.getInstance("SSL");
    	sc.init(null, new TrustManager[]{new X509TrustManager() {
    	    @Override
			public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
    	 
    	    }
    	 
    	    @Override
			public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
    	 
    	    }
    	 
    	    @Override
			public X509Certificate[] getAcceptedIssuers() {
    	        return null;
    	    }
    	}}, new SecureRandom());
    	httpClient.setSslSocketFactory(sc.getSocketFactory());
    	httpClient.setHostnameVerifier(new HostnameVerifier() {
    	    @Override
			public boolean verify(final String hostname, final SSLSession session) {
    	        return true;
    	    }
    	});
	}
    private static String userAgent() {
        String javaVersion = "Java/" + System.getProperty("java.version");
        String os = System.getProperty("os.name") + " "+ System.getProperty("os.arch") + " " + System.getProperty("os.version");
        return "httpclient (" + os + ") " + javaVersion;
    }

    private static RequestBody create(final MediaType contentType,
                                      final byte[] content, final int offset, final int size) {
        if (content == null) {
			throw new NullPointerException("content == null");
		}

        return new RequestBody() {
            
            @Override
			public MediaType contentType() {
                return contentType;
            }

            
            @Override
			public long contentLength() {
                return size;
            }

            
            @Override
			public void writeTo(final BufferedSink sink) throws IOException {
                sink.write(content, offset, size);
            }
        };
    }

    /**
     * 发送任意请求
     * @return 
     * @throws IOException 
     * @throws UnsupportedEncodingException 
     */
    public Response RTS(final String method, final URI url,final String data, final HashMap<String,String> headers) throws UnsupportedEncodingException, IOException {
		// TODO Auto-generated method stub
    	if (method.equals("POST")) {
        	return this.post(url.toString(), data.getBytes("UTF-8"), headers);
		} 
        else if (method.equals("PUT")) {
        	return this.put(url.toString(), data, headers);
		} 
        else if (method.equals("GET")) {
        	String _url=url.toString();
        	_url+=_url.indexOf('?')>0?("&"+data):("?"+data);
			return this.get(_url,headers);
		} 
        else if (method.equals("DELETE")) {
        	return this.delete(url.toString(), data, headers);
		}else{
			return null;
		}
	}
    
    public Response get(final String url) throws IOException{
        return get(url, new HashMap<String,String>());
    }
    
    public String getBody(final String url){
    	String result="";
    	try {
    		ResponseBody res = get(url).body();
    		result=res.string();
    		res.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
    	return result;
    }
    
    public String getBody(final String url,final Map<String,String>param){
    	return getBody(buildGetUrl(url,param));
    }
    
    /**
     * 仿post方式构造url.
     * @param uri
     * @param param
     * @return
     */
    private String buildGetUrl(final String uri,final Map<String,String>param){
    	String url=uri;
    	StringBuffer sb = new StringBuffer();
		if (param != null) {
			Set<String> set = param.keySet();
			for (String string : set) {
				sb.append("&").append(string).append("=").append(param.get(string));
			}
		}
		String realUrl = null;
		if (sb.length() > 0) {
			if (url.contains("&")) {
				realUrl = url + sb.toString();
			} else {
				realUrl = url.endsWith("?") ? url : url + "?";
				realUrl += sb.substring(1);
			}
		} else {
			realUrl = url;
		}
		return realUrl;
    }

    public Response get(final String url, final HashMap<String,String> headers) throws IOException{
        Request.Builder requestBuilder = new Request.Builder().get().url(url);
        return send(requestBuilder, headers);
    }

    public Response post(final String url, final String body ) throws IOException{
        return post(url, body.getBytes("utf-8"), null, DefaultMime);
    }

    public Response post(final String url, final byte[] body, final HashMap<String,String> headers) throws UnsupportedEncodingException, IOException{
        return post(url, body , headers, DefaultMime);
    }

    public Response post(final String url, final HashMap<String,String> params, final HashMap<String,String> headers) throws IOException{
        final FormEncodingBuilder f = new FormEncodingBuilder();
        Set<Entry<String, String>> set = params.entrySet();
        for (Entry<String, String> entry : set) {
			f.add(entry.getKey(), entry.getValue());
		}
        return post(url, f.build(), headers);
    }

    public Response post(final String url, final byte[] body, final HashMap<String,String> headers, final String contentType) throws IOException{
        RequestBody rbody;
        if (body != null && body.length > 0) {
            MediaType t = MediaType.parse(contentType);
            rbody = RequestBody.create(t, body);
        } else {
            rbody = RequestBody.create(null, new byte[0]);
        }
        return post(url, rbody, headers);
    }

    public Response post(final String url, final byte[] body, final int offset, final int size,
                         final HashMap<String,String> headers, final String contentType) throws IOException{
        RequestBody rbody;
        if (body != null && body.length > 0) {
            MediaType t = MediaType.parse(contentType);
            rbody = create(t, body, offset, size);
        } else {
            rbody = RequestBody.create(null, new byte[0]);
        }
        return post(url, rbody, headers);
    }

    private Response post(final String url, final RequestBody body, final HashMap<String,String> headers) throws IOException{
        Request.Builder requestBuilder = new Request.Builder().url(url).post(body);
        return send(requestBuilder, headers);
    }

    public Response put(final String url, final byte[] body, final HashMap<String,String> headers) throws IOException{
        return put(url, body, headers, DefaultMime);
    }

    public Response put(final String url, final String body, final HashMap<String,String> headers) throws UnsupportedEncodingException, IOException{
        return put(url, body.getBytes("utf-8"), headers, DefaultMime);
    }

    public Response put(final String url, final HashMap<String,String> params, final HashMap<String,String> headers) throws IOException{
        final FormEncodingBuilder f = new FormEncodingBuilder();
        Set<Entry<String, String>> set = params.entrySet();
        for (Entry<String, String> entry : set) {
			f.add(entry.getKey(), entry.getValue());
		}
        return put(url, f.build(), headers);
    }

    public Response put(final String url, final byte[] body, final HashMap<String,String> headers, final String contentType) throws IOException{
        RequestBody rbody;
        if (body != null && body.length > 0) {
            MediaType t = MediaType.parse(contentType);
            rbody = RequestBody.create(t, body);
        } else {
            rbody = RequestBody.create(null, new byte[0]);
        }
        return put(url, rbody, headers);
    }

    public Response put(final String url, final byte[] body, final int offset, final int size,
                         final HashMap<String,String> headers, final String contentType) throws IOException{
        RequestBody rbody;
        if (body != null && body.length > 0) {
            MediaType t = MediaType.parse(contentType);
            rbody = create(t, body, offset, size);
        } else {
            rbody = RequestBody.create(null, new byte[0]);
        }
        return put(url, rbody, headers);
    }

    
    private Response put(final String url, final RequestBody body, final HashMap<String,String> headers) throws IOException{
        Request.Builder requestBuilder = new Request.Builder().url(url).put(body);
        return send(requestBuilder, headers);
    }
    

    public Response delete(final String url, final byte[] body, final HashMap<String,String> headers) throws IOException{
        return delete(url, body, headers, DefaultMime);
    }

    public Response delete(final String url, final String body, final HashMap<String,String> headers) throws UnsupportedEncodingException, IOException{
        return delete(url, body.getBytes("utf-8"), headers, DefaultMime);
    }

    public Response delete(final String url, final HashMap<String,String> params, final HashMap<String,String> headers) throws IOException{
        final FormEncodingBuilder f = new FormEncodingBuilder();
        Set<Entry<String, String>> set = params.entrySet();
        for (Entry<String, String> entry : set) {
			f.add(entry.getKey(), entry.getValue());
		}
        return delete(url, f.build(), headers);
    }

    public Response delete(final String url, final byte[] body, final HashMap<String,String> headers, final String contentType) throws IOException{
        RequestBody rbody;
        if (body != null && body.length > 0) {
            MediaType t = MediaType.parse(contentType);
            rbody = RequestBody.create(t, body);
        } else {
            rbody = RequestBody.create(null, new byte[0]);
        }
        return delete(url, rbody, headers);
    }

    public Response delete(final String url, final byte[] body, final int offset, final int size,
                         final HashMap<String,String> headers, final String contentType) throws IOException{
        RequestBody rbody;
        if (body != null && body.length > 0) {
            MediaType t = MediaType.parse(contentType);
            rbody = create(t, body, offset, size);
        } else {
            rbody = RequestBody.create(null, new byte[0]);
        }
        return delete(url, rbody, headers);
    }

    private Response delete(final String url, final RequestBody body, final HashMap<String,String> headers) throws IOException{
        Request.Builder requestBuilder = new Request.Builder().url(url).delete(body);
        return send(requestBuilder, headers);
    }
    
    public Response multipartPost(final String url,
                                  final HashMap<String,String> fields,
                                  final String name,
                                  final String fileName,
                                  final byte[] fileBody,
                                  final String mimeType,
                                  final HashMap<String,String> headers) throws IOException{
        RequestBody file = RequestBody.create(MediaType.parse(mimeType), fileBody);
        return multipartPost(url, fields, name, fileName, file, headers);
    }

    public Response multipartPost(final String url,
                                  final HashMap<String,String> fields,
                                  final String name,
                                  final String fileName,
                                  final File fileBody,
                                  final String mimeType,
                                  final HashMap<String,String> headers) throws IOException{
        RequestBody file = RequestBody.create(MediaType.parse(mimeType), fileBody);
        return multipartPost(url, fields, name, fileName, file, headers);
    }

    private Response multipartPost(final String url,
                                   final HashMap<String,String> fields,
                                   final String name,
                                   final String fileName,
                                   final RequestBody file,
                                   final HashMap<String,String> headers) throws IOException{
        final MultipartBuilder mb = new MultipartBuilder();
        mb.addFormDataPart(name, fileName, file);
        Set<Entry<String, String>> set = fields.entrySet();
        for (Entry<String, String> entry : set) {
			mb.addFormDataPart(entry.getKey(), entry.getValue());
		}
        mb.type(MediaType.parse("multipart/form-data"));
        RequestBody body = mb.build();
        Request.Builder requestBuilder = new Request.Builder().url(url).post(body);
        return send(requestBuilder, headers);
    }

    public Response send(final Request.Builder requestBuilder, final HashMap<String,String> headers) throws IOException{
        if (headers != null) {
            Set<Entry<String, String>> set = headers.entrySet();
            for (Entry<String, String> entry : set) {
				requestBuilder.header(entry.getKey(), entry.getValue());
			}
        }

        requestBuilder.header("User-Agent", userAgent());
        long start = Const.getSysTimestamp();
        com.squareup.okhttp.Response res = null;
        double duration = (Const.getSysTimestamp() - start) / 1000.0;
        IpTag tag = new IpTag();
        try {
            res = httpClient.newCall(requestBuilder.tag(tag).build()).execute();
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        }
        return res;
    }

    public void asyncSend(final Request.Builder requestBuilder, final HashMap<String,String> headers, final AsyncCallback cb) {
        if (headers != null) {
        	Set<Entry<String, String>> set = headers.entrySet();
            for (Entry<String, String> entry : set) {
				requestBuilder.header(entry.getKey(), entry.getValue());
			}
        }

        requestBuilder.header("User-Agent", userAgent());
        final long start = Const.getSysTimestamp();
        IpTag tag = new IpTag();
        httpClient.newCall(requestBuilder.tag(tag).build()).enqueue(new Callback() {
            
            @Override
			public void onFailure(final Request request, final IOException e) {
                e.printStackTrace();
                long duration = (Const.getSysTimestamp() - start) / 1000;
                cb.complete(null, e.getMessage(), duration);
            }

            
            @Override
			public void onResponse(final com.squareup.okhttp.Response response) throws IOException {
                long duration = (Const.getSysTimestamp() - start) / 1000;
                cb.complete(response, "", duration);
                response.code();
            }
        });
    }

    public void asyncPost(final String url, final byte[] body, final int offset, final int size,
                          final HashMap<String,String> headers, final String contentType, final AsyncCallback cb) {
        RequestBody rbody;
        if (body != null && body.length > 0) {
            MediaType t = MediaType.parse(contentType);
            rbody = create(t, body, offset, size);
        } else {
            rbody = RequestBody.create(null, new byte[0]);
        }

        Request.Builder requestBuilder = new Request.Builder().url(url).post(rbody);
        asyncSend(requestBuilder, headers, cb);
    }

    public void asyncMultipartPost(final String url,
                                   final HashMap<String,String> fields,
                                   final String name,
                                   final String fileName,
                                   final byte[] fileBody,
                                   final String mimeType,
                                   final HashMap<String,String> headers,
                                   final AsyncCallback cb) {
        RequestBody file = RequestBody.create(MediaType.parse(mimeType), fileBody);
        asyncMultipartPost(url, fields, name, fileName, file, headers, cb);
    }

    public void asyncMultipartPost(final String url,
                                   final HashMap<String,String> fields,
                                   final String name,
                                   final String fileName,
                                   final File fileBody,
                                   final String mimeType,
                                   final HashMap<String,String> headers,
                                   final AsyncCallback cb){
        RequestBody file = RequestBody.create(MediaType.parse(mimeType), fileBody);
        asyncMultipartPost(url, fields, name, fileName, file, headers, cb);
    }
    private void asyncMultipartPost(final String url,
                                    final HashMap<String,String> fields,
                                    final String name,
                                    final String fileName,
                                    final RequestBody file,
                                    final HashMap<String,String> headers,
                                    final AsyncCallback cb) {
        final MultipartBuilder mb = new MultipartBuilder();
        mb.addFormDataPart(name, fileName, file);
        Set<Entry<String, String>> set = fields.entrySet();
        for (Entry<String, String> entry : set) {
			mb.addFormDataPart(entry.getKey(), entry.getValue());
		}
		 
        mb.type(MediaType.parse("multipart/form-data"));
        RequestBody body = mb.build();
        Request.Builder requestBuilder = new Request.Builder().url(url).post(body);
        asyncSend(requestBuilder, headers, cb);
    }

    private static class IpTag {
        public String ip = null;
    }
    
    /**
     * 异步请求时的回调类
     * @author Administrator
     *
     */
    public static interface AsyncCallback {
    	/**
    	 * 发送异步请求是回调操作
    	 * @param response 响应信息(响应失败时,该对象为空)
    	 * @param message 响应失败的信息(响应成功时,该对象为空)
    	 * @param duration 时间戳
    	 */
        void complete(Response response, String message, long duration);
    }
}
